"""
重返未来1999卡牌信息识别代码
"""

import numpy as np
import pytesseract
import pyautogui
import cv2
#识别星级所用的hsv范围
Color_lower = np.array([4, 100, 100])
Color_upper = np.array([8, 255, 255])

"""
下面这些hsv范围是之前第一版卡牌识别用到的
第一版效果非常不好，误扫严重，但还是放着没删，可以做参考()
"""
Green_lower = np.array([30,100,100])
Green_upper = np.array([100,255,255])

Blue_lower = np.array([100,60,46])
Blue_upper = np.array([124,200,200])

Orange_lower = np.array([11,43,46])
Orange_upper = np.array([25,255,255])

#用来处理图像的腐蚀和膨胀
kernel = np.ones((5, 5), np.uint8) # 创建一个5*5大小的核

"""
Read_Template_Image读取模版函数(第二版卡牌识别用到的函数)
传参说明
    n1---代表角色(如0代表远旅)
    n2---代表该角色具体技能，游戏中每个角色三种卡(包含至终仪式)
返回
    函数会返回请求的模版图片
"""
def Read_Template_Image(n1,n2):
    if n1 == 0:#远旅
        if n2 == 0:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/yuanlv/yuanlv_attack.jpg")
        elif n2 == 1:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/yuanlv/yuanlv_counter.jpg")
        elif n2 == 2:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/yuanlv/ceremony.jpg")
    elif n1 == 1:#槲寄生
        if n2 == 0:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/hujisheng/hujisheng_attack0.jpg")
        elif n2 == 1:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/hujisheng/hujisheng_attack1.jpg")
        elif n2 == 2:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/hujisheng/ceremony.jpg")
    elif n1 == 2:#红弩箭
        if n2 == 0:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/hongnujian/hongnujian_attack0.jpg")
        elif n2 == 1:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/hongnujian/hongnujian_attack1.jpg")
        elif n2 == 2:
            image = cv2.imread("/Users/a1/Desktop/Game1999/main/template_images/hongnujian/ceremony.jpg")
    #返回模版图片
    return image
"""
define_card_style确定卡牌类别函数
传参说明
    i,j的传参对应Read_Template_Image函数里的内容
    用于在识别完卡牌后对卡牌信息整合，方便后续处理
返回
    函数会返回卡牌类型，用于后续枚举可能，评分
"""
def define_card_style(i,j):
    if i == 0:
        if j == 0:
            return "c0"
        elif j == 1:
            return "c1"
        elif j == 2:
            return "c2"
    elif i == 1:
        if j == 0:
            return "b0"
        elif j == 1:
            return "b1"
        elif j == 2:
            return "b2"
    elif i == 2:
        if j == 0:
            return "a0"
        elif j == 1:
            return "a1"
        elif j == 2:
            return "a2"
"""
determine_inclusion_relationship确认包含关系函数(第二版卡牌识别用到的函数)
传参说明
    xmin---矩形的x最小值
    ymin---矩形的y最小值
    xmax---矩形的x最大值
    ymax---矩形的y最大值
    point_pos---"卡牌确定点"坐标
返回
    函数会判断xmin,ymin,xmax,ymax构成的矩形是否包括住了point_pos"卡牌确定点"坐标
    返回True或者False
"""
def determine_inclusion_relationship(xmin,ymin,xmax,ymax,point_pos):
    if xmin <= point_pos[0] and xmax >= point_pos[0]:
        if ymin <= point_pos[1] and ymax >= point_pos[1]:#判断是否包括确定点
            return True#包含
    return False#不包含
"""
Get_Card_Text_Information获取卡牌底部文字信息函数(第一版卡牌识别方案，已废弃)
传参说明:
    text_image---传入的卡牌底部文字图片
返回
    函数会返回识别到的卡牌文字信息，并以card_attribute_list列表返回
"""
def Get_Card_Text_Information(text_image):
    text_information = pytesseract.image_to_boxes(text_image)
    
    length_list = len(list(text_information))
    card_information_list = []
    count = 0
    state = True  # state变量用于控制是否瞬间记录状态
    for j in range(0, length_list):
        if state == True:
            count = j
        if text_information[j] == "\n":
            state = True
            append_list = []
            strings = "" # 该字符串用于记录数字
            for k in range(count, j):
                strings += text_information[k]
            append_list = strings.split(' ')
            
            card_information_list.append(append_list)
        else:
            state = False

    judgment_string = ""
    card_attribute_list = []
    
    for i in range(0,len(card_information_list)):

        judgment_string  = judgment_string + card_information_list[i][0]
        """
        考虑到安卓手机传过来的像素较低，识别文字可能会漏掉
        这里根据识别一些关键字母组合判断卡牌文字(容错)
        """
        if "ac" in judgment_string:
            
            judgment_string = ""
            card_attribute_list.append("Attack")
        elif "ebuff" in judgment_string:
            
            judgment_string = ""
            card_attribute_list.append("Debuff")
        elif "ou" in judgment_string:
            
            judgment_string = ""
            card_attribute_list.append("Counter")

    card_attribute_list.reverse()#倒装列表
    #返回
    return card_attribute_list
"""
Get_Card_Level获取卡牌等级函数
传参说明:
    level_image---传入卡牌上方等级图片
返回:
    返回识别到的卡牌等级card_level_list列表
"""
def Get_Card_Level(level_image):
    hsv = cv2.cvtColor(level_image, cv2.COLOR_BGR2HSV)  # HSV颜色设置
    mask = cv2.inRange(hsv, Color_lower, Color_upper)#提取在HSV范围内的图像
    mask_copy = mask.copy()#复制，用于对比观察

    mask_copy = cv2.dilate(mask_copy, np.ones((3, 3), np.uint8), iterations=1)  # 再次膨胀图像，增加容错率

    mask = cv2.dilate(mask, kernel, iterations=2)  # 膨胀
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)#识别轮廓
    divide_roi_pos = []#划分ROI区域的列表，存储坐标

    if len(contours) > 0:#如果识别到了
        for i in range(0,len(contours)):
            x, y, w, h = cv2.boundingRect(contours[i])  # 通过循环分别找到四个角的轮廓
            xmin = x - w
            ymax = y - h
            xmax = x + w
            ymin = y + h
            divide_roi_pos.append([xmin,ymin,xmax,ymax])#添加信息

    divide_roi_pos.sort(key=lambda x:x[0])#将识别结果排序，方便坐标信息处理
    divide_roi_image_list = []#存储图像

    #读取divide_roi_pos里的坐标，用循环切割图像存入divide_roi_image_list列表
    for j in range(0,len(divide_roi_pos)):
        level_image = cv2.rectangle(level_image,(int(divide_roi_pos[j][0]),int(divide_roi_pos[j][3])),
                                  (int(divide_roi_pos[j][2]),int(divide_roi_pos[j][1])), (0, 255, 255), 2)
        #切割图像获取ROI
        divide_roi_image = mask_copy[int(divide_roi_pos[j][3]):int(divide_roi_pos[j][1]),int(divide_roi_pos[j][0]):int(divide_roi_pos[j][2])]
        
        divide_roi_image_list.append(divide_roi_image)#存储图像

    card_level_list = []
    #对分割出的图像识别轮廓，判断每个卡牌的等级
    for k in range(0,len(divide_roi_image_list)):
        contours, hierarchy = cv2.findContours(divide_roi_image_list[k], cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)  # 识别轮廓
        card_level_list.append(len(contours))#此时轮廓数量等于卡牌星级

    card_level_list.reverse()#倒装列表
    return card_level_list#返回
"""
count_contour_area计算轮廓面积函数(第一版卡牌类型判断方案，已废弃)
传参说明:
    mask---二值化后的图像
返回:
    函数会返回面积大小
"""
def count_contour_area(mask):
    area = 0
    #其中i,j代表列行，用来遍历整个二值化图片(23*43像素)
    for i in range(0, 23):
        for j in range(0, 43):
            if mask[i][j] == 255:#若像素点为白
                area += 1
    return area
"""
detect_ownership识别卡牌所属类型人物函数(第一版卡牌类型判断方案，已废弃)
传参说明:
    image---卡牌图片
返回:
    函数会返回ownership_list所属人物类型列表
"""
def detect_ownership(image):
    xmin = 13
    ymin = 56
    xmax = 56
    ymax = 33
    area_list = []
    ownership_list = []
    for i in range(0,7):
        new_card_roi = image[ymax:ymin,xmin:xmax]
        hsv = cv2.cvtColor(new_card_roi, cv2.COLOR_BGR2HSV)
        #分别对不同颜色情况获取二值化图片，根据颜色判断所属人物类型
        blue_mask = cv2.inRange(hsv,Blue_lower,Blue_upper)
        green_mask = cv2.inRange(hsv,Green_lower,Green_upper)
        orange_mask = cv2.inRange(hsv,Orange_lower,Orange_upper)
        #添加面积信息
        area_list.append(count_contour_area(blue_mask))
        area_list.append(count_contour_area(green_mask))
        area_list.append(count_contour_area(orange_mask))
        #循环遍历判断哪个颜色的占比(面积)最大
        max_area = -1
        for k in range(0,len(area_list)):
            area = area_list[k]
            if area > max_area:
                max_area = area
        print(area_list.index(max_area))

        if area_list.index(max_area) == 0:#远旅
            
            style = "c"
        elif area_list.index(max_area) == 1:#槲寄生
            
            style = "b"
        elif area_list.index(max_area) == 2:#红弩箭
            
            style = "a"

        ownership_list.append(style)
        area_list = []#重制面积列表
        xmin += 73;xmax += 73#切换到下一张卡牌的坐标

    ownership_list.reverse()#倒装列表

    return ownership_list

"""
detect_owner_new识别卡牌所属类型人物函数(第二版卡牌识别用到的函数)
传参说明
    image---卡牌图片
返回
    函数会返回ownership_list所属人物类型列表
"""
def detect_owner_new(image):
    anchor_points = []#"卡牌确定点"坐标列表
    ownership_list = []
    card_dict = {"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0}#卡牌存在漏扫情况，所以先要保证列表信息存在
    
    #创建卡牌定位点坐标列表
    for k in range(0,7):
        anchor_points.append([41+k*75,46])#这里需要自己修改坐标
    """
    i表示人物类型,j表示该人物具体卡牌
    每次循环遍历每张卡牌在整个图像中的识别情况
    卡牌模板图片在template_images文件夹中，用Read_Template_Image读取，最后用define_card_style整合卡牌信息
    模板匹配用的opencv里的matchTemplate函数，方法用的cv2.TM_CCOEFF_NORMED(准确度更高，计算量会更大)
    为了增加容错，这里的精度设置的低了点，为0.7(卡牌的相似度只有超过70%才会保留信息)
    """
    for i in range(0,3):
        for j in range(0,3):
            
            template = Read_Template_Image(i,j)
            theight, twidth = template.shape[:2]

            result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)#模版匹配
            threshold = 0.7#精度设置
            loc = np.where(result >= threshold)#进行筛选
            determine_matrix = []
            for min_loc in zip(*loc[::-1]):#遍历
                
                determine_matrix.append([min_loc[0],min_loc[1],
                                       min_loc[0] + twidth, min_loc[1] + theight])#将待确定的矩阵存储信息
                cv2.rectangle(image, min_loc, (min_loc[0] + twidth, min_loc[1] + theight), (0, 255, 225), 2)#画出矩阵

            for matrix_count in range(0,len(determine_matrix)):#遍历识别框矩阵
                for count in range(1,8):#遍历7张卡牌
                    anchor_point = [anchor_points[count - 1][0], anchor_points[count - 1][1]]  # 定位点坐标获取
                    determine_result = determine_inclusion_relationship(determine_matrix[matrix_count][0],
                        determine_matrix[matrix_count][1],determine_matrix[matrix_count][2],
                                        determine_matrix[matrix_count][3],anchor_point)#确定矩阵是否处于"卡牌确定点"
                    if determine_result == True:
                        
                        card_dict[str(count)] = define_card_style(i,j)#整合卡牌信息

    for s in range(1,8):#整合7张卡牌的信息
        ownership_list.append(card_dict[str(s)])
    
    ownership_list.reverse()#倒装列表
    return ownership_list
"""
下面的程序为调试detect_card程序的测试程序，不影响主函数运行，单独运行该文件时才会触发
"""
if __name__ == "__main__":
    image = pyautogui.screenshot(region=[960, 50, 960, 430])  # x,y,w,h
    image = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR)
    # 分割图像分别处理
    ROI_Image = image[318:429, 401:930]
    Text_Image = image[416:429, 384:970]
    Level_Image = image[318:330, 401:930]
    Ownership_Image = image[330:416, 401:950]

    ownership_list = detect_owner_new(Ownership_Image)
    print(ownership_list)
    #cv2.imshow("owner",Ownership_Image)
    #cv2.imshow("template", template)
    cv2.waitKey(0)


